#ifndef xpack_utils_mix_buffer
#define xpack_utils_mix_buffer
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::utils_mix_buffer::inc
#include"adapter/random_access.hpp"
#include"algo/addressing.hpp"
#include"define/base_type.hpp"
#include"dumb/disable_copy.hpp"
#include"instruction/index_of_first_set.hpp"
#include"interface/can_callback.hpp"
#include"interface/seqptr.hpp"
#include"interface/unified_seq.hpp"
#include"macro/xdefer.hpp"
#include"macro/xexport.hpp"
#include"memop/copy.hpp"
#include"math/minimum.hpp"
#include"utils/memory.hpp"
#include"utils/unique_array.hpp"
#pragma pop_macro("xuser")

namespace mixc::utils_mix_buffer{
    template<class itemx_t, uxx first_page_length_v, uxx temp_buffer_length_v = first_page_length_v>
    requires((first_page_length_v & (first_page_length_v - 1)) == 0 and first_page_length_v > 0)
    xstruct(
        xtmpl(mix_buffer, itemx_t, first_page_length_v),
        xpubb(inc::disable_copy),
        xpubb(inc::random_access<mix_buffer<itemx_t, first_page_length_v, temp_buffer_length_v>, itemx_t>),
        xprif(m_first_page,     u08[sizeof(itemx_t) * first_page_length_v]),
        xprif(m_static_buffer,  u08[sizeof(itemx_t) * temp_buffer_length_v]),
        xprif(m_table,          itemx_t * [32]),
        xprif(m_i_tab,          uxx),
        xprif(m_line_size,      uxx),
        xprif(m_length,         uxx),
        xprif(m_current,        itemx_t *),
        xprif(m_end,            itemx_t *),
        xprif(m_in_use,         bool)
    )
    public:
        xprops()
            xpubget(length, uxx);
        $

        xprops_operator()

    private:
        static inline constexpr auto is_static_buffer = generic_option_t(true);

        struct free{
            void operator()(itemx_t * mem, uxx length, auto unique){
                if (the_t * self = nullptr; (*unique)->addition_bit == is_static_buffer){
                    self                = (the_t *)(uxx(mem) - uxx(& self->m_static_buffer));
                    self->m_in_use      = false;
                    return;
                }

                inc::memory::default_free<itemx_t>(mem, length);
            }
        };
    public:
        using final_t                   = the_t;
        using item_t                    = itemx_t;
        using itemp_t                   = item_t *;
        using lend_result_type          = inc::unique_array<item_t, free>;

        mix_buffer():
            m_i_tab{0},
            m_line_size{ first_page_length_v },
            m_length{},
            m_current(itemp_t(m_first_page)),
            m_end(itemp_t(m_first_page) + first_page_length_v),
            m_in_use(false) {

            m_table[0]                  = itemp_t(m_first_page);
            m_table[1]                  = nullptr;
        }

        ~mix_buffer(){
            this->clear();
        }

        // 约定：
        // 禁止使用任何拷贝
        // 因为 m_static_buffer 的所有权会随着原副本的失效而无法正常使用

        void push(item_t const & value){
            auto seq                    = inc::seqptr<item_t>(xmodify(value), 1);
            this->push_core(seq);
        }

        void push(item_t const * list, uxx length){
            auto seq                    = inc::seqptr<item_t>((item_t *)list, length);
            this->push_core(seq);
        }

        void push(inc::seqptr<item_t> const & list){
            this->push_core(list);
        }

        template<class seq_t>
        requires(inc::can_unified_seqlizex<seq_t, item_t>)
        void push(seq_t const & list){
            this->push_core(inc::unified_seq<seq_t>(list));
        }

        lend_result_type lend(uxx length){
            // 约定：
            // 禁止使用任何拷贝
            // 因为 m_static_buffer 的所有权会随着原副本的失效而无法正常使用
            item_t * buffer             = nullptr;

            if (auto rest = uxx(m_end - m_current); rest >= length){
                buffer                  = m_current;
                m_length               += length;
                m_current              += length;

                if (m_current == m_end){
                    this->alloc_new_page();
                }
                return lend_result_type(buffer, ::length(length), not need_destruction, not need_free, not is_static_buffer);
            }
            else if (m_in_use == false and length <= temp_buffer_length_v){
                m_in_use                = true;
                buffer                  = (item_t *)m_static_buffer;
                return lend_result_type(buffer, ::length(length), need_destruction, need_free, is_static_buffer);
            }
            else{
                buffer                  = inc::memory::alloc<item_t>(xsizeof(item_t) * length);
                return lend_result_type(buffer, ::length(length), need_destruction, need_free, not is_static_buffer);
            }
        }

        template<class invoke_t>
        requires(inc::can_callback<invoke_t, void(item_t * content, uxx length)>)
        void foreach_level(invoke_t && invoke){
            foreach_level([&](inc::seqptr<item_t> seq){
                invoke((item_t *)seq, seq->length);
            });
        }

        template<class invoke_t>
        requires(inc::can_callback<invoke_t, void(inc::seqptr<item_t>)>)
        void foreach_level(invoke_t && invoke){
            auto inital_size            = first_page_length_v;
            auto line_size              = first_page_length_v;
            auto seq                    = inc::seqptr<item_t>{};

            for(uxx i = 0; i <= m_i_tab; i++, invoke(seq)/*输出*/){
                if (i != m_i_tab){
                    seq                 = inc::seqptr<item_t>(m_table[i], line_size);
                    line_size           = inital_size;
                    inital_size        *= 2;
                }
                else{
                    seq                 = inc::seqptr<item_t>(m_table[i], m_current - m_table[i]);
                }
            }
        }

        template<class seq_t>
        requires(inc::can_seqptrlizex<seq_t, item_t>)
        void copy_to(seq_t const & list){
            this->copy_to_core(inc::unified_seq<seq_t>(list));
        }

        void copy_to(item_t const * list, uxx length){
            auto seq                    = inc::seqptr<item_t>(list, length);
            this->copy_to_core(seq);
        }

        void clear(){
            if (m_length == 0){
                return;
            }
            else{
                m_length                = 0;
            }

            auto inital_size            = first_page_length_v;
            auto last_size              = first_page_length_v;
            auto line_size              = first_page_length_v;

            for(uxx i = 0, page_length; i <= m_i_tab; i++){
                if (page_length = line_size; i != m_i_tab){
                    last_size           = line_size;
                    line_size           = inital_size;
                    inital_size        *= 2;
                }
                else{
                    last_size           = m_current - m_table[m_i_tab];
                }

                for(uxx j = 0; j < last_size; j++){
                    m_table[i][j].~item_t();
                }

                if (i != 0){
                    inc::memory::free(m_table[i], xsizeof(item_t) * page_length);
                }
            }

            // 让索引还原，避免一直追加元素时索引递增
            xnew(this) the_t;
        }

    private:
        template<class seq_t>
        void push_core(seq_t list){
            auto length                 = uxx(list->length);
            auto rest_buffer            = uxx(m_end - m_current);
            auto can_push               = inc::minimum<uxx>(rest_buffer, length);
            auto rest_push              = length - can_push;

            for(uxx i = 0; i < can_push; i++){
                xnew(m_current + i) item_t(list[i]);
            }

            if (m_length += length; rest_push == 0){
                m_current              += length;
                return;
            }

            for(uxx line_size;;){
                // 先获取 m_line_size，alloc_new_page() 会修改此值
                if (line_size = m_line_size, this->alloc_new_page(); rest_push < line_size){
                    for(uxx i = 0; i < rest_push; i++){
                        xnew(m_current + i) item_t(list[i + can_push]);
                    }

                    m_current          += rest_push;
                    break;
                }
                // 如果当前分配的缓冲区不够大，只能存缓冲区的大小 line_size
                else{
                    for(uxx i = 0; i < line_size; i++){
                        xnew(m_current + i) item_t(list[i + can_push]);
                    }

                    can_push           += line_size;
                    rest_push          -= line_size;
                }
            }
        }

        template<class seq_t>
        void copy_to_core(seq_t list){
            auto inital_size            = first_page_length_v;
            auto line_size              = first_page_length_v;
            auto rest                   = uxx(list->length);
            auto copy_size              = uxx{};

            for(uxx i = 0; i <= m_i_tab and rest != 0; i++, rest -= copy_size){
                if (i != m_i_tab){
                    copy_size           = inc::minimum<uxx>(line_size, rest);
                    inc::copy_with_operator_unsafe(list, m_table[i], copy_size);
                    list                = list.backward(copy_size);
                    line_size           = inital_size;
                    inital_size        *= 2;
                }
                else{
                    line_size           = m_current - m_table[i];
                    copy_size           = inc::minimum<uxx>(line_size, rest);
                    inc::copy_with_operator_unsafe(list, m_table[i], copy_size);
                }
            }
        }

        void alloc_new_page(){
            m_table[m_i_tab += 1]       = inc::memory::alloc<item_t>(xsizeof(item_t) * m_line_size);
            m_table[m_i_tab + 1]        = nullptr;
            m_current                   = m_table[m_i_tab];
            m_end                       = m_current + m_line_size;
            m_line_size                *= 2;
        }

    private:
        friend inc::random_access<the_t, itemx_t>;

        item_t & random_get(uxx i) const {
            auto mask                   = ~(first_page_length_v - 1);
            auto guide                  = inc::addressing::var(mask, i);
            auto i_first_page_bit       = inc::index_of_first_set(first_page_length_v);
            return m_table[guide.page - i_first_page_bit][guide.offset];
        }
    $
}

namespace mixc::utils_mix_buffer::origin{
    template<class item_t, uxx first_page_length_v = 128, uxx temp_buffer_length_v = first_page_length_v>
    using mix_buffer = 
        mixc::utils_mix_buffer::mix_buffer<
            item_t, 
            first_page_length_v, 
            temp_buffer_length_v
        >;
}

#endif

xexport_space(mixc::utils_mix_buffer::origin)
